﻿using com.nm.Helper;
using com.nm.interfaces;
using System;
using System.Collections.Generic;
using System.Linq;

namespace com.nm.library.Factorys
{
    /// <summary>
    /// 把配置信息存储到 Redis
    /// </summary>
    public class RedisConfigurationFactory : IConfigurationFactory
    {
        private static RedisClient _client;
        private static RedisClient GetClient => _client ?? (_client = new RedisClient(6));

        /// <summary>
        /// 命名空间
        /// </summary>
        public string NameSpace { get; set; }

        /// <summary>
        /// 得到字符串形式的配置值（就是把值转换成字符串）
        /// </summary>
        /// <param name="pKey"></param>
        /// <returns></returns>
        public string GetValue(string pKey)
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return GetClient.GetString(pKey);
            }
            return string.Empty;
        }

        /// <summary>
        /// 设置配置值
        /// 必须是已经存在的 否则设置不成功
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public IConfigurationFactory SetConfig(string pKey, string pValue)
        {
            var key = getKey(pKey);
            if (!GetClient.KeyExists(key))
            {
                throw new Exception($"{pKey} 不存在");
            }
            var type = Type.GetType(GetClient.GetString($"{key}:type"));
            if (type == typeof(int))
            {
                GetClient.SetString(key, int.Parse(pValue));
            }
            else if (type == typeof(long))
            {
                GetClient.SetString(key, long.Parse(pValue));
            }
            else if (type == typeof(double))
            {
                GetClient.SetString(key, double.Parse(pValue));
            }
            else if (type == typeof(bool))
            {
                GetClient.SetString(key, bool.Parse(pValue));
            }
            else if (type == typeof(string))
            {
                GetClient.SetString(key, pValue);
            }
            else if (type == typeof(DateTime))
            {
                GetClient.SetString(key, DateTime.Parse(pValue));
            }
            else if (type == typeof(decimal))
            {
                GetClient.SetDecimal(key, decimal.Parse(pValue));
            }

            return this;
        }

        /// <summary>
        /// 得到配置项目的证实 key
        /// </summary>
        /// <param name="pKey"></param>
        /// <returns></returns>
        protected string getKey(string pKey)
        {
            if (string.IsNullOrWhiteSpace(NameSpace))
            {
                return $"ac:{pKey}";
            }
            if (this.NameSpace.EndsWith(":"))
            {
                return $"ac:{NameSpace}{pKey}";
            }
            return $"ac:{NameSpace}:{pKey}";
        }

        /// <summary>
        /// 返回所有的可用键值
        /// </summary>
        public IEnumerable<string> AllKey
        {
            get
            {
                if (string.IsNullOrWhiteSpace(NameSpace))
                {
                    return GetClient.GetValuesFromSet($"ack:keys", 0, 999).Select(i => i.ToString());
                }
                if (this.NameSpace.EndsWith(":"))
                {
                    return GetClient.GetValuesFromSet($"ack:{NameSpace}keys", 0, 999).Select(i => i.ToString());
                }
                return GetClient.GetValuesFromSet($"ack:{NameSpace}:keys", 0, 999).Select(i => i.ToString());
            }
        }

        private void SetKey(string pKey)
        {
            if (string.IsNullOrWhiteSpace(NameSpace))
            {
                GetClient.AddValueToSet($"ack:keys", pKey);
            }
            else if (this.NameSpace.EndsWith(":"))
            {
                GetClient.AddValueToSet($"ack:{NameSpace}keys", pKey);
            }
            else
            {
                GetClient.AddValueToSet($"ack:{NameSpace}:keys", pKey);
            }
        }

        #region Get

        /// <summary>
        /// 获取配置值，如果值不存在则自动创建
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public int Get(string pKey, int pValue)
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return GetClient.GetInt(key);
            }
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetInt(key, pValue);
            return pValue;
        }

        /// <summary>
        /// 获取配置值，如果值不存在则自动创建
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public string Get(string pKey, string pValue)
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return GetClient.GetString(key);
            }
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return pValue;
        }

        /// <summary>
        /// 获取配置值，如果值不存在则自动创建
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public double Get(string pKey, double pValue)
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return GetClient.GetDouble(key);
            }
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return pValue;
        }

        /// <summary>
        /// 获取配置值，如果值不存在则自动创建
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public long Get(string pKey, long pValue)
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return GetClient.GetLong(key);
            }
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return pValue;
        }

        /// <summary>
        /// 获取配置值，如果值不存在则自动创建
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public decimal Get(string pKey, decimal pValue)
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return GetClient.GetDecimal(key, pValue);
            }
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetDecimal(key, pValue);
            return pValue;
        }

        /// <summary>
        /// 获取配置值，如果值不存在则自动创建
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public DateTime Get(string pKey, DateTime pValue)
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return new DateTime(GetClient.GetLong(key));
            }
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return pValue;
        }

        /// <summary>
        /// 获取配置值，如果值不存在则自动创建
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public bool Get(string pKey, bool pValue)
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return (bool)GetClient.GetString(key, pValue);
            }
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return pValue;
        }

        /// <summary>
        /// 获取配置值，如果值不存在则自动创建
        /// </summary>
        /// <typeparam name="T">可实例化的类</typeparam>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public T Get<T>(string pKey, T pValue) where T : class, new()
        {
            var key = getKey(pKey);
            if (GetClient.KeyExists(key))
            {
                return GetClient.GetString(key).ToString().FromJson<T>();
            }
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue.ToJson());
            return pValue;
        }

        #endregion Get

        #region Set

        /// <summary>
        /// 设置配置值，如果已经存在则会覆盖
        /// 如果已经存在值且类型不符合则会抛出异常
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public IConfigurationFactory Set<T>(string pKey, T pValue) where T : class, new()
        {
            var key = getKey(pKey);
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue.ToJson());
            return this;
        }

        /// <summary>
        /// 设置配置值，如果已经存在则会覆盖
        /// 如果已经存在值且类型不符合则会抛出异常
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public IConfigurationFactory Set(string pKey, int pValue)
        {
            var key = getKey(pKey);
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return this;
        }

        /// <summary>
        /// 设置配置值，如果已经存在则会覆盖
        /// 如果已经存在值且类型不符合则会抛出异常
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public IConfigurationFactory Set(string pKey, long pValue)
        {
            var key = getKey(pKey);
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return this;
        }

        /// <summary>
        /// 设置配置值，如果已经存在则会覆盖
        /// 如果已经存在值且类型不符合则会抛出异常
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public IConfigurationFactory Set(string pKey, decimal pValue)
        {
            var key = getKey(pKey);
            SetKey(pKey);
            GetClient.SetDecimal(key, pValue);
            return this;
        }

        /// <summary>
        /// 设置配置值，如果已经存在则会覆盖
        /// 如果已经存在值且类型不符合则会抛出异常
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public IConfigurationFactory Set(string pKey, bool pValue)
        {
            var key = getKey(pKey);
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return this;
        }

        /// <summary>
        /// 设置配置值，如果已经存在则会覆盖
        /// 如果已经存在值且类型不符合则会抛出异常
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public IConfigurationFactory Set(string pKey, DateTime pValue)
        {
            var key = getKey(pKey);
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return this;
        }

        /// <summary>
        /// 设置配置值，如果已经存在则会覆盖
        /// 如果已经存在值且类型不符合则会抛出异常
        /// </summary>
        /// <param name="pKey"></param>
        /// <param name="pValue"></param>
        /// <returns></returns>
        public IConfigurationFactory Set(string pKey, double pValue)
        {
            var key = getKey(pKey);
            SetKey(pKey);
            GetClient.SetString($"{key}:type", pValue.GetType().ToString());
            GetClient.SetString(key, pValue);
            return this;
        }

        #endregion Set
    }
}